package ep.io;

import java.io.EOFException;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.util.Scanner;

import ep.model.Ant;
import ep.model.Board;

public class Reader {

	private Board board;
	private Ant z;
	private int iteractions;
	private int numberOfProcessors;
	private int bitshift = -1;
	private int bits;
	private int width = -1;
	private int height = -1;


	public Reader(String[] args) throws IOException {

		this.board = readBoard(args[0]);
		this.z = Ant.parse(args[1]);
		this.iteractions = Integer.parseInt(args[2]);
		this.numberOfProcessors = Integer.parseInt(args[3]);
	}

	private Board readBoard(String filePath) throws IOException {

		File file = new File(filePath);
		Scanner scanner;
		try {
			scanner = new Scanner(file);

			String type = getLine(scanner);
			String line = getLine(scanner);
			String[] dimensions = line.split(" ");
			int height = Integer.parseInt(dimensions[1]);
			int width = Integer.parseInt(dimensions[0]);

			if(type.equals("P1")){
				return readP1(scanner, height, width);
			}else{
				scanner.close();
				return readP4(file, height, width);
			}
		} catch (FileNotFoundException e) {
			e.printStackTrace();
			return null;
		}
	}

	private Board readP4(File file, int height, int width) throws FileNotFoundException, IOException {

		Board board = new Board(height + 2, width + 2);

		InputStream in = extractComments(file);
		readHeader(in);

		for (int x = 0; x < height; x++) {
			int i=0;
			while( i < width){
				for (int j = i; j < i+8; j++) {
					Boolean b = readBit(in);
					if( j < width)
						if(b) board.turnOn(x+1, j+1);
				}

				i+=8;
			}
		}
		completeBoard(board);
		return board;
	}

	private FileInputStream extractComments(File file) throws FileNotFoundException,
	IOException {
		Scanner scanner = new Scanner(file);

		OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("temp"));

		while(scanner.hasNextLine()){
			String line = scanner.nextLine();
			if( !line.startsWith("#") )
				osw.write(line+"\n");
		}
		osw.close();
		scanner.close();
		return new FileInputStream("temp");
	}

	private Board readP1(Scanner scanner, int height, int width) {
		Board board = new Board( height + 2, width + 2);
		//String line;
		for (int i = 1; i <= height; i++) {
			//line = getLine(scanner);
			//String[] pos = line.split(" ");
			for (int j = 1; j <= width; j++) {
				char c = nextChar(scanner);
				System.out.print(c);
				if( c == '1')
					board.turnOn(i, j);
			}
			System.out.println();
		}
		completeBoard(board);
		scanner.close();
		return board;
	}

	private String scanned = "";
	
	private char nextChar(Scanner scanner) {
		if( scanned.length() == 0){
			scanned = scanner.nextLine();
			while( scanned.startsWith("#") ){
				scanned = scanner.nextLine();
			}
		}
		char c = scanned.charAt(0);
		scanned = scanned.substring(1);
		while( c != '0' && c!= '1'){
			c = scanned.charAt(0);
			scanned = scanned.substring(1);
		}
		return c;
	}

	private void completeBoard(Board board) {
		for(int i = 1; i < board.getHeight(); i++){
			if(board.getPositionState(i, 1) == 1)
				board.turnOn(i, 0);
			if(board.getPositionState(i, board.getWidth()-2) == 1)
				board.turnOn(i, board.getWidth()-1);
		}
		for(int i = 1; i < board.getWidth(); i++){
			if(board.getPositionState(1, i) == 1)
				board.turnOn(0, i);
			if(board.getPositionState(board.getHeight()-2,i) == 1)
				board.turnOn(board.getHeight()-1,i);
		}
	}

	void readHeader( InputStream in ) throws IOException
	{
		char c1;

		c1 = (char) readByte( in );
		readByte( in );

		if ( c1 != 'P' )
			throw new IOException( "not a PBM/PGM/PPM file" );

		width = readInt( in );
		height = readInt( in );

	}

	private static char readChar( InputStream in ) throws IOException
	{
		char c;

		c = (char) readByte( in );
		if ( c == '#' )
		{
			do
			{
				c = (char) readByte( in );
			}
			while ( c != '\n' && c != '\r' );
		}

		return c;
	}

	private static char readNonwhiteChar( InputStream in ) throws IOException
	{
		char c;

		do
		{
			c = readChar( in );
		}
		while ( c == ' ' || c == '\t' || c == '\n' || c == '\r' );

		return c;
	}

	private static int readInt( InputStream in ) throws IOException
	{
		char c;
		int i;

		c = readNonwhiteChar( in );
		if ( c < '0' || c > '9' )
			throw new IOException( "junk in file where integer should be" );

		i = 0;
		do
		{
			i = i * 10 + c - '0';
			c = readChar( in );
		}
		while ( c >= '0' && c <= '9' );

		return i;
	}

	int getWidth()
	{
		return width;
	}

	int getHeight()
	{
		return height;
	}

	private static int readByte( InputStream in ) throws IOException
	{
		int b = in.read();
		if ( b == -1 )
			throw new EOFException();

		return b;
	}

	private boolean readBit( InputStream in ) throws IOException
	{
		if ( bitshift == -1 )
		{
			bits = readByte( in );
			bitshift = 7;
		}
		boolean bit = ( ( ( bits >> bitshift ) & 1 ) != 0 );
		--bitshift;
		return bit;
	}

	String getLine(Scanner scanner){
		String s = scanner.nextLine();
		while(s.startsWith("#"))
			s = scanner.nextLine();
		return s;
	}

	public Board getBoard() {
		return board;
	}

	public Ant getZ() {
		return z;
	}

	public int getIteractions() {
		return iteractions;
	}

	public int getNumberOfProcessors() {
		return numberOfProcessors;
	}

}
